package com.happe.foodeasy.helper;

import com.happe.foodeasy.constraint.annotation.IdCustom;
import com.happe.foodeasy.exception.BeanClassNoIdException;
import org.apache.commons.beanutils.BasicDynaBean;
import org.apache.commons.beanutils.BasicDynaClass;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.PropertyUtils;

import java.beans.IntrospectionException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * This class is used to deal target class's relation annotation, for example ManyToOneCustom,
 * OneToOneCustom and so on, and generate new class object and instance at runtime.
 * Created by wubin on 2016-05-08.
 */
public class DealRelationalAnnotationWrapper<T> {
    private Class<T> targetClass;
    private T targetBean;
    private BasicDynaClass resultClass;
    private BasicDynaBean resultBean;

    public DealRelationalAnnotationWrapper(Class<T> targetClass, T targetBean) {
        this.targetClass = targetClass;
        this.targetBean = targetBean;
    }

    public BasicDynaClass retrieveDealClass() {
        return this.resultClass;
    }

    public BasicDynaBean retrieveDealBean() {
        return this.resultBean;
    }

    public void startDeal() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, IntrospectionException {
        Field[] fields = ClassOperateHelper.retrieveFieldsBelongProperty(this.targetClass);
        Object[] values = new Object[fields.length];
        DynaProperty[] dynaProperties = new DynaProperty[fields.length];
        for (int i = 0; i < fields.length; i++) {
            if (AnnotationHelper.checkRelationalAnnotationForField(fields[i])) {
                values[i] = retrieveIdFromAnnotationField(fields[i]);
                dynaProperties[i] = new DynaProperty(fields[i].getName() + "Id", Long.class);
            } else {
                values[i] = PropertyUtils.getProperty(this.targetBean, fields[i].getName());
                dynaProperties[i] = new DynaProperty(fields[i].getName(), fields[i].getType());
            }
        }

        BasicDynaClass dynaClass = new BasicDynaClass(this.targetClass.getSimpleName(), null, dynaProperties);
        BasicDynaBean dynaBean = new BasicDynaBean(dynaClass);
        for (int i = 0; i < dynaProperties.length; i++) {
            dynaBean.set(dynaProperties[i].getName(), values[i]);
        }

        this.resultClass = dynaClass;
        this.resultBean = dynaBean;
    }


    private Object retrieveIdFromAnnotationField(Field targetField) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Object annotationObject = PropertyUtils.getProperty(this.targetBean, targetField.getName());
        Field[] fields = ClassOperateHelper.retrieveFieldsBelongProperty(annotationObject.getClass());
        Object target = null;
        for (Field fieldOne : fields) {
            if (fieldOne.isAnnotationPresent(IdCustom.class)) {
                target = PropertyUtils.getProperty(annotationObject, fieldOne.getName());
                break;
            }
        }
        if (target == null) {
            throw new BeanClassNoIdException(annotationObject.getClass().getName() + "中无法找到id注解");
        }
        return target;
    }

    public void setTargetClass(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    public void setTargetBean(T targetBean) {
        this.targetBean = targetBean;
    }
}
